#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <asm/uaccess.h>  // for copy_to_user and copy_from_user

#include <uapi/linux/fs.h>

#include "simple_item.h"

#define DEVNAME "items"

#define LOGI(fmt, args...) \
	pr_info(DEVNAME "[%04d][@%s]" fmt, __LINE__, __func__, ##args)

#define LOGE(fmt, args...) \
	pr_err(DEVNAME "[%04d][@%s]" fmt, __LINE__, __func__, ##args)


#define MAX_SIZE (ITEM_MAX_LEN*2 * ITEM_MAX_COUNT)
static char gbuff[MAX_SIZE];
static int rsize = 0;

static void store(const char *key, const char *val)
{
	rsize += snprintf(gbuff+rsize, MAX_SIZE-rsize, "%s=%s\n", key, val);
}

static void item_buffer_init(void)
{
	rsize +=snprintf(gbuff+rsize, MAX_SIZE-rsize, "%s\n", ITEM_START_KEY);
	item_traverse(store);
	rsize +=snprintf(gbuff+rsize, MAX_SIZE-rsize, "%s\n", ITEM_END_KEY);
}
static int item_dev_open(struct inode *node, struct file *flip)
{

	return 0;
}

static int item_dev_release(struct inode *node, struct file *flip)
{
	return 0;
}

static ssize_t item_dev_write(struct file *filep, const char __user *buff, size_t count ,loff_t *fpos)
{

	return count;
}


static ssize_t item_dev_read(struct file *filp, char __user *buff, size_t count, loff_t *fpos)
{
	LOGI("count=%lu, fpos=%llu", count, *fpos);
	if(*fpos >= rsize){   
		return 0; // end of file
	}
	if(count > rsize){
		count = rsize;
	}
	if(copy_to_user(buff, gbuff, count)){
		LOGE("copy_to_user fail\n");
		return -EFAULT;
	}
	*fpos += count;
	return count;  // 0 indicate end of file
}

loff_t item_dev_llseek (struct file *fp, loff_t fpos, int when)
{
	loff_t rv = 0;
	switch(when){
	case SEEK_SET:
		rv = 0;
		break;
	case SEEK_CUR:
		rv = fpos;
		break;
	case SEEK_END:
		rv = rsize;
		break;
	default:
		rv = fpos;
		break;
	}
	return rv;
}
static const struct file_operations item_dev_fops = {
/* .owner = THIS_MODULE, */
	.open = item_dev_open,
	.release = item_dev_release,
	.read = item_dev_read,
	.write = item_dev_write,
	.llseek = item_dev_llseek,
};

static struct miscdevice item_dev_misc_device = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVNAME,
	.fops = &item_dev_fops,
};

#ifdef ITEM_DEV_TEST
int item_dev_test_init(void)
{

	if (misc_register(&item_dev_misc_device)) {
		return -1;
	}
	item_buffer_init();
	return 0;
}
void item_dev_test_exit(void)
{
	misc_deregister(&item_dev_misc_device);
}

#else
static int __init item_dev_init(void)
{
	LOGI("++");
	if (misc_register(&item_dev_misc_device)) {
		return -1;
	}
	item_buffer_init();
	return 0;
}

static void __exit item_dev_exit(void)
{
	LOGI("++");
	misc_deregister(&item_dev_misc_device);
}


module_init(item_dev_init);
module_exit(item_dev_exit);

MODULE_AUTHOR("tj <tj_wust@163.com>");
MODULE_DESCRIPTION("item driver");
MODULE_LICENSE("GPL");
#endif